#ifndef EXEC_CONTEXT_HPP
#define EXEC_CONTEXT_HPP

#ifdef _MSC_VER
#pragma once
#endif

#include "jam.hpp"
#include "jam_lists.hpp"
#include "rules.hpp"
#include "jam_ast.hpp"

#include <deque>
#include <boost/noncopyable.hpp>

#include <google/dense_hash_map>
#define jam_hash_map google::dense_hash_map

#define JAM_ROOT_MODULE_NAME "_ROOT_"

class jam_context;

///////////////////////////////////////////////////////////////////////////////////////
////// Module
///////////////////////////////////////////////////////////////////////////////////////
class JAMLIB_EXPORT jam_module : public boost::noncopyable, public ref_count_holder
{
public:
	struct rule_ent
	{
		jam_rule_ptr rule_;
		bool is_localized_;
		bool is_exported_;
	};
	typedef jam_hash_map<jam_string,rule_ent> rule_hash;
	typedef jam_hash_map<jam_string,jam_module*> module_hash;
	typedef jam_hash_map<jam_string,jam_list> var_hash_t;

	jam_module(jam_context *context, bool is_user, const jam_string &name) : 
		context_(context), is_user_module_(is_user), name_(name) {init_hash();}
	jam_module() {init_hash();}

	const jam_string& get_name() const {return name_;}

	const jam_list& get_var_value(const jam_string &name,bool only_local=false) const;
	jam_list& get_var_value_ref(const jam_string &name);

	void set_single_var_value(const jam_string &name,const jam_string &val, 
		assignment_operation oper=ASSIGN_SET);
	void set_var_value(const jam_string &name,const jam_list &val, 
		assignment_operation oper=ASSIGN_SET);

	void register_rule(jam_rule_ptr rule, bool is_exported);	
	void import_rule(jam_rule_ptr rule, const jam_string &name, bool localize);	
	bool find_rule(const jam_string &name, rule_ent *res, bool exported_search=false);
	bool export_rule(const jam_string &name);

	void import_module(jam_module* module);

	void clear();

private:
	void init_hash();

	jam_context *context_;
	jam_string name_; //Module name
	var_hash_t vars_; //Module variables
	rule_hash rules_; //Rules registered in this module
	module_hash imported_names_; //List of imported names
	bool is_user_module_; //Flag indicating the module was loaded from a user .jam-file.
};
DECLARE_INTRUSIVE_PTR(jam_module);

///////////////////////////////////////////////////////////////////////////////////////
////// Variable saver.
///////////////////////////////////////////////////////////////////////////////////////
class JAMLIB_EXPORT var_saver
{
	union
	{
		char buf_[sizeof(jam_list)];
		boost::type_with_alignment<boost::alignment_of<jam_list>::value>::type align;
	} saved_var_value_buf;
	jam_list *saved_var_place;

	jam_list* get_list() {return reinterpret_cast<jam_list*>(&saved_var_value_buf.buf_[0]);}
public:
	var_saver();
	var_saver(jam_list *val);	
	void set_val(jam_list *val);

	~var_saver();
};

///////////////////////////////////////////////////////////////////////////////////////
////// Call frame
///////////////////////////////////////////////////////////////////////////////////////
struct jam_frame
{	
	jam_frame(jam_context &context, jam_frame *prev, const parse_node_base *node);
	jam_frame(jam_context &context, jam_module_ptr root, const parse_node_base *node);
	
	jam_context &context_;

	jam_frame *prev_;
	jam_frame *prev_user_; //nearest enclosing frame for which module->user_module_ is true.	
	const parse_node_base *node_; //Parse tree node of the expression
	const parse_node_base *cur_node_; //Current executing node (updated in evaluate_rule)
	jam_module_ptr module_; //Caller module
	jam_string scope_name_; //Scope name, used for error reporting and debug
	
	list_of_lists args_;	
};

///////////////////////////////////////////////////////////////////////////////////////
////// Context
///////////////////////////////////////////////////////////////////////////////////////
class JAMLIB_EXPORT jam_context : public ref_count_holder
{
	friend class jam_interpeter;

	jam_hash_map<jam_string,jam_module_ptr> modules_;
	std::ostream *out_,*err_; //Input and output streams

	jam_context();
public:
	//Createas a new context
	static boost::intrusive_ptr<jam_context> new_context();
	//Redirect IO
	void set_streams(std::ostream *out,std::ostream *err);

	bool bind_rule(const jam_string &name, jam_module_ptr module, jam_module::rule_ent *res);
	jam_module_ptr bind_module(const jam_string &name, bool is_user=true);
	jam_module_ptr get_root_module();

	void intepret_tree(const parse_node_ptr &node, jam_module_ptr module,jam_list *res);
	void intepret_tree(const parse_node_ptr &node, jam_module_ptr module, 
		jam_frame *frame, jam_list *res);

	//IO
	std::ostream& err(){return *err_;}
	std::ostream& out(){return *out_;}

	//Debug routines
	void backtrace_line(const jam_frame *frame);
	void backtrace(const jam_frame *frame);
	void print_source_line(const parse_node_base &p);
};
DECLARE_INTRUSIVE_PTR(jam_context);

#endif //EXEC_CONTEXT_HPP
